#include "stdafx.h"
#ifdef LIT_OGRE_SUPPORT
#include <LitMeshDescriptionManager.h>

namespace Lit
{
	MeshDescriptionFile::MeshDescriptionFile( const MeshDescriptionFile &rk ){}
	MeshDescriptionFile::MeshDescriptionFile(Ogre::ResourceManager* creator, const Ogre::String &name, 
						Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual, 
						Ogre::ManualResourceLoader *loader)
		: Ogre::Resource(creator, name, handle, group, isManual, loader)
	{
		/* If you were storing a pointer to an object, then you would set that pointer to NULL here.
		*/
 
		/* For consistency with StringInterface, but we don't add any parameters here
		That's because the Resource implementation of StringInterface is to
		list all the options that need to be set before loading, of which 
		we have none as such. Full details can be set through scripts.
		*/ 
		createParamDictionary(MeshDescriptionManager::RESOURCE_TYPE);
	}
 
	MeshDescriptionFile::~MeshDescriptionFile()
	{
		unload();
	}
 
	bool MeshDescriptionFile::isDynamic() const{ return mDynamic; }
	Physics::ShapeType MeshDescriptionFile::getShape() const{ return mShape; }
	Ogre::Real MeshDescriptionFile::getMass() const{ return mMass; }
	const Lit::Vec3& MeshDescriptionFile::getFacingDirection() const{ return mFacingDir; }
	const Lit::Vec3& MeshDescriptionFile::getOffset() const{ return mOffset; }
	const String& MeshDescriptionFile::getMesh() const{ return mMesh; }
	const String& MeshDescriptionFile::getMaterial() const{ return mMesh; }

	// farm out to SceneSerializer
	void MeshDescriptionFile::loadImpl()
	{
		MeshDescriptionSerializer serializer;
		Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource(mName, mGroup, true, this);
		serializer.importDescription(stream, this);
	}
 
	void MeshDescriptionFile::unloadImpl()
	{
		/* If you were storing a pointer to an object, then you would check the pointer here,
		and if it is not NULL, you would destruct the object and set its pointer to NULL again.
		*/

	}
 
	size_t MeshDescriptionFile::calculateSize() const
	{
		return sizeof(*this);
	}
	MeshDescriptionFilePtr::MeshDescriptionFilePtr() : Ogre::SharedPtr<MeshDescriptionFile>() {}
	MeshDescriptionFilePtr::MeshDescriptionFilePtr(MeshDescriptionFile *rep) : Ogre::SharedPtr<MeshDescriptionFile>(rep) {}
	MeshDescriptionFilePtr::MeshDescriptionFilePtr(const MeshDescriptionFilePtr &r) : Ogre::SharedPtr<MeshDescriptionFile>(r) {} 
	MeshDescriptionFilePtr::MeshDescriptionFilePtr(const Ogre::ResourcePtr &r) : Ogre::SharedPtr<MeshDescriptionFile>()
	{
		if( r.isNull() )
			return;
		// lock & copy other mutex pointer
		OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)
		OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)
		pRep = static_cast<MeshDescriptionFile*>(r.getPointer());
		pUseCount = r.useCountPointer();
		useFreeMethod = r.freeMethod();
		if (pUseCount)
		{
			++(*pUseCount);
		}
	}
 
	/// Operator used to convert a ResourcePtr to a ScenePtr
	MeshDescriptionFilePtr& MeshDescriptionFilePtr::operator=(const Ogre::ResourcePtr& r)
	{
		if(pRep == static_cast<MeshDescriptionFile*>(r.getPointer()))
			return *this;
		release();
		if( r.isNull() )
			return *this; // resource ptr is null, so the call to release above has done all we need to do.
		// lock & copy other mutex pointer
		OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)
		OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)
		pRep = static_cast<MeshDescriptionFile*>(r.getPointer());
		pUseCount = r.useCountPointer();
		useFreeMethod = r.freeMethod();
		if (pUseCount)
		{
			++(*pUseCount);
		}
		return *this;
	}
	MeshDescriptionSerializer::MeshDescriptionSerializer()
	{
	}
 
	MeshDescriptionSerializer::~MeshDescriptionSerializer()
	{
	}
 
	void MeshDescriptionSerializer::exportDescription(const MeshDescriptionFile *pDesc, const Ogre::String &fileName)
	{
		TiXmlDocument doc(fileName.c_str());
		doc.LinkEndChild( new TiXmlDeclaration( PROJECT_VERSION_STRING, "UTF-8", "yes" ) );
			
		TiXmlElement * rootElem = new TiXmlElement( "MeshDefinition" );
		rootElem->SetAttribute( "Mesh", pDesc->getMesh().c_str() );
			
		TiXmlElement * geoElem = new TiXmlElement( "Geometry" );
		TiXmlElement * matElem = new TiXmlElement( "Material" );
		TiXmlElement * phyElem = new TiXmlElement( "Physics" );
			
		geoElem->SetAttribute( "FacingDir", Ogre::StringConverter::toString(pDesc->getFacingDirection()).c_str() );
			
		matElem->SetAttribute( "Name", pDesc->getMaterial().c_str() );
			
		phyElem->SetAttribute( "Shape", String(trShapeType( pDesc->getShape() )).c_str() );
		phyElem->SetAttribute( "Dynamic", Ogre::StringConverter::toString( pDesc->isDynamic(), false ).c_str() );
		phyElem->SetAttribute( "Mass", Ogre::StringConverter::toString( pDesc->getMass() ).c_str() );
		phyElem->SetAttribute( "Offset", Ogre::StringConverter::toString(pDesc->getOffset()).c_str() );

		rootElem->LinkEndChild( geoElem );
		rootElem->LinkEndChild( matElem );
		rootElem->LinkEndChild( phyElem );
		doc.LinkEndChild( rootElem );

		doc.SaveFile();
	}
 
	void MeshDescriptionSerializer::importDescription(Ogre::DataStreamPtr &stream, MeshDescriptionFile *pDest)
	{
		TiXmlDocument doc;
		String str = stream->getAsString();
		const char *err = doc.Parse( str.c_str() );
		if( doc.Error() )
			Ogre::LogManager::getSingleton().logMessage( "Cannot load mesh description, invalid format in :\r\n" + str );
		TiXmlElement *pRoot = doc.RootElement();
			
		if( !pRoot || (String(pRoot->Value()) != "MeshDefinition") ) 
		OGRE_EXCEPT( Ogre::Exception::ERR_INVALIDPARAMS, "MeshDefinition node not found or is not the Root node", "MeshDescriptionSerializer::importDescription" );
			
		String mesh = pRoot->Attribute("Mesh");
		if( mesh.empty() ) OGRE_EXCEPT( Ogre::Exception::ERR_INVALIDPARAMS, "No Mesh attribute specified for Root node while parsing "+mesh+" definition", "MeshDescriptionSerializer::importDescription" );
			
		pDest->mMesh = mesh;
		TiXmlElement *geoNode = pRoot->FirstChildElement("Geometry");
		TiXmlElement *matNode = pRoot->FirstChildElement("Material");
		TiXmlElement *phyNode = pRoot->FirstChildElement("Physics");
		if( !geoNode ) OGRE_EXCEPT( Ogre::Exception::ERR_INVALIDPARAMS, "Geometry node not found", "MeshDescriptionSerializer::importDescription" );
		if( !matNode ) OGRE_EXCEPT( Ogre::Exception::ERR_INVALIDPARAMS, "Material node not found", "MeshDescriptionSerializer::importDescription" );
		if( !phyNode ) OGRE_EXCEPT( Ogre::Exception::ERR_INVALIDPARAMS, "Physics node not found", "MeshDescriptionSerializer::importDescription" );
			
		String facdir = geoNode->Attribute("FacingDir"),
					matnam = matNode->Attribute("Name"),
					mass = phyNode->Attribute("Mass"),
					offset = phyNode->Attribute("Offset"),
					shape = phyNode->Attribute("Shape"),
					dyn = phyNode->Attribute("Dynamic");
			
		if( facdir.empty() ) OGRE_EXCEPT( Ogre::Exception::ERR_INVALIDPARAMS, "Geometry::FacingDir attribute not found", "MeshDescriptionSerializer::importDescription" );
		if( matnam.empty() ){
			Ogre::LogManager::getSingleton().logMessage("Custom material not found using mesh's material !");
		}
		if( mass.empty() ){
			mass = "1.0";
			Ogre::LogManager::getSingleton().logMessage("No mass found, setting mass to 1.0");
		}
		if( offset.empty() ){
			offset = Ogre::StringConverter::toString( Lit::Vec3::ZERO );
			Ogre::LogManager::getSingleton().logMessage("No offset found, setting to Vector3::ZERO");
		}
		if( shape.empty() ){
			shape = String(trShapeType( Physics::ST_BOX ));
			Ogre::LogManager::getSingleton().logMessage("No physics shape found, setting to "+String(trShapeType( Physics::ST_BOX )) );
		}
		if( dyn.empty() ){
			dyn = "false";
			Ogre::LogManager::getSingleton().logMessage("No dynamic property found, setting to false");
		}
			
		pDest->mMaterial = matnam;
		pDest->mMass = Ogre::StringConverter::parseReal(mass);
		pDest->mOffset = Ogre::StringConverter::parseVector3(offset); 
		pDest->mDynamic = Ogre::StringConverter::parseBool(dyn); 

		Physics::ShapeType shapet = Physics::ST_NULL;
		if( shape == trShapeType( Physics::ST_BOX ) ) shapet = Physics::ST_BOX;
		if( shape == trShapeType( Physics::ST_SPHERE ) ) shapet = Physics::ST_SPHERE;
		if( shape == trShapeType( Physics::ST_PLANE ) ) shapet = Physics::ST_PLANE;
			
		pDest->mShape = shapet;
			
		pDest->mFacingDir = Vec3::UNIT_X;
		if( facdir == "UNIT_X" ) pDest->mFacingDir = Vec3::UNIT_X;
		else if( facdir == "UNIT_Y" ) pDest->mFacingDir = Vec3::UNIT_Y;
		else if( facdir == "UNIT_Z" ) pDest->mFacingDir = Vec3::UNIT_Z;
		else if( facdir == "NEGATIVE_UNIT_X" ) pDest->mFacingDir = Vec3::NEGATIVE_UNIT_X;
		else if( facdir == "NEGATIVE_UNIT_Y" ) pDest->mFacingDir = Vec3::NEGATIVE_UNIT_Y;
		else if( facdir == "NEGATIVE_UNIT_Z" ) pDest->mFacingDir = Vec3::NEGATIVE_UNIT_Z;
		else Ogre::LogManager::getSingleton().logMessage("No facing direction found, setting default val to Vector3::UNIT_X");
		Ogre::LogManager::getSingleton().logMessage("Definition for mesh '"+mesh+"' successfully loaded");
	}
 
	MeshDescriptionManager *MeshDescriptionManager::ms_singleton = 0;
	MeshDescriptionManager *MeshDescriptionManager::getSingletonPtr()
	{
		assert(ms_singleton);  
		return(ms_singleton);
	}
 
	MeshDescriptionManager &MeshDescriptionManager::getSingleton()
	{  
		static MeshDescriptionManager inst;
		if( !ms_singleton ) ms_singleton = &inst;
		return *ms_singleton;
	}
	String MeshDescriptionManager::RESOURCE_TYPE = String("Mesh Definition");

	MeshDescriptionManager::MeshDescriptionManager()
	{
		mResourceType = RESOURCE_TYPE;
 
		// low, because it will likely reference other resources
		mLoadOrder = 30.0f;
 
		// this is how we register the ResourceManager with OGRE
		Ogre::ResourceGroupManager::getSingleton()._registerResourceManager(mResourceType, this);
		Ogre::ResourceGroupManager::getSingleton()._registerScriptLoader(this);
	}
 
	MeshDescriptionManager::~MeshDescriptionManager()
	{
		// and this is how we unregister it
		Ogre::ResourceGroupManager::getSingleton()._unregisterScriptLoader(this);
		Ogre::ResourceGroupManager::getSingleton()._unregisterResourceManager(mResourceType);
	}
	
	MeshDescriptionFilePtr MeshDescriptionManager::load(const Ogre::String &name, const Ogre::String &group)
	{
		MeshDescriptionFilePtr textf = getByName(name);
 
		if (textf.isNull())
			textf = create(name, group);
		textf->load();
		return textf;
	}
	Ogre::Resource *MeshDescriptionManager::createImpl(const Ogre::String &name, Ogre::ResourceHandle handle, 
												const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader, 
												const Ogre::NameValuePairList *createParams)
	{
		return new MeshDescriptionFile (this, name, handle, group, isManual, loader);
	}	
	const Ogre::StringVector& MeshDescriptionManager::getScriptPatterns(void) const{
		static Ogre::StringVector ret;
		if( ret.empty() )
		{
			ret.push_back("*.meshdef");
		}
		return ret;
	}
	/** Parse a script file.
	@param stream Weak reference to a data stream which is the source of the script
	@param groupName The name of a resource group which should be used if any resources
		are created during the parse of this script.
	*/
	void MeshDescriptionManager::parseScript(Ogre::DataStreamPtr& stream, const Ogre::String& groupName)
	{
		Ogre::LogManager::getSingleton().logMessage("Parsing Mesh Definition file "+stream->getName());
		MeshDescriptionFilePtr textf = getByName(stream->getName());
 
		if (textf.isNull())
			textf = create(stream->getName(), groupName);
		stream->close();
		textf->load();
	}

	/** Gets the relative loading order of scripts of this type.
	@remarks
		There are dependencies between some kinds of scripts, and to enforce
		this all implementors of this interface must define a loading order. 
	@returns A value representing the relative loading order of these scripts
		compared to other script users, where higher values load later.
	*/
	Ogre::Real MeshDescriptionManager::getLoadingOrder(void) const{ return 600.0f; }
};

#endif